#include "InstanceOvertime.h"

const char *InstanceOvertime::SUBDIR = "/overtime";

InstanceOvertime::InstanceOvertime()
:Instance() { }

InstanceOvertime::~InstanceOvertime() { }

bool InstanceOvertime::load( const char *name ) {

	// Initializing name
	if (name == NULL)
		throw new Exception("Invalid name");

	this->name = name;

	// Opening file...
	char *path = getPath(name);
	Input in( path );

	delete [] path;

	// File is open. Declare variables...
	int je = 0;
	int *tmp;

	// Reading data...
	J.read(in);
	T.read(in);
	M.read(in);

	// Reading setup cost...
	ArrayDoubleParam stmp(J, 'j', "Setup Cost");
	//setupCost = ArrayDoubleParam(J, 'j', "Setup Cost");
	setupCost = stmp;
	setupCost.read(in);

	// Reading holding cost...
	holdingCost = ArrayDoubleParam(J, 'j', "Holding Cost");
	holdingCost.read(in);

	// Reading overtime cost...
	overtimeCost = MatrixDoubleParam( J, T, 'j', 't', "Overtime Cost");
	overtimeCost.read(in);

	// Reading demand...
	demand = MatrixDoubleParam( J, T, 'j', 't', "Demand" );
	demand.read(in);

	// Reading resource cost...
	resourceCost = MatrixDoubleParam( J, J, 'j', 'k', "Resource Cost" );
	resourceCost.read(in);

	tmp = new int[J];
	successor = MatrixIntParam( J, 'j', "Successor" );
	successorCount = ArrayIntParam( J, 'j', "Successor Count" );

	for (int j = 0; j < J; j++) {
		successorCount[j] = 0;

		for (int k = 0; k < J; k++)
			if (resourceCost[j][k] > 0)
				tmp[ successorCount[j]++ ] = k;

		if (successorCount[j] > 0) {
			successor[j] = ArrayIntParam( successorCount[j], 'i', "Successor" );
			for (int k = 0; k < successorCount[j]; k++)
				successor[j][k] = tmp[k];
		}
	}

	delete [] tmp;

	// Reading capacity cost...
	capacityCost = MatrixDoubleParam( M, J, 'm', 'j', "Capacity Cost" );
	capacityCost.read(in);

	prodMachine = ArrayIntParam( J, 'j', "Producing Machine" );
	for (int j = 0; j < J; j++) {
		for (int m = 0; m < M; m++) {
			prodMachine[j] = -1;
			if (capacityCost[m][j] > 0) {
				if (prodMachine[j] == -1)
					prodMachine[j] = m;
				else
					throw new Exception("Instance has two machines producing the same product");
			}
		}
	}

	// Reading setup time...
	setupTime = MatrixDoubleParam( M, J, 'm', 'j', "Setup Time" );

	// Reading capacity...
	capacity = MatrixDoubleParam( M, T, 'm', 't', "Capacity" );
	capacity.read(in);

	// Calculating echelon holding cost
	eHoldingCost = ArrayDoubleParam( J, 'j', "Echelon Holding Cost" );
	for (int j = 0; j < J; j++) {
		eHoldingCost[j] = holdingCost[j];
		for (int k = 0; k < J; k++) {
			eHoldingCost[j] -= resourceCost[k][j] * holdingCost[k];
		}
	}

	// Calculating echelon demand
	eDemand = MatrixDoubleParam( J, T, 'j', 't', "Echelon Demand" );
	for (int j = 0; j < J; j++)
		for (int t = 0; t < T; t++)
			eDemand[j][t] = calcEchelonDemand(j, t);

	// Calculating upper bounds.
	upperBound = MatrixDoubleParam( J, T, 'j', 't', "Upper Bound" );
	double value;
	int index;

	for (int j = 0; j < J; j++)
		for (int t = 0; t < T; t++) {
			index = j*T + t;
			upperBound[j][t] = calcTotalEchelonDemand(j, t);
			
			for (int m = 0; m < M; m++) {
				value = (capacity[m][t] - setupTime[m][j])/capacityCost[m][j];
				if (value < upperBound[j][t])
					upperBound[j][t] = value;
			}
		}

	// Calculating (l, S) demands.
	lsDemand = CubeDoubleParam( J, T, 'j', 't', "LS Demand" );

	for (int j = 0; j < J; j++) {
		for (int l = 0; l < T; l++) {
			lsDemand[j][l] = ArrayDoubleParam( l+1, 't', "LS Demand" );
			for (int t = 0; t <= l; t++) {
				lsDemand[j][l][t] = 0;
				for (int p = t; p <= l; p++)
					lsDemand[j][l][t] += eDemand[j][p];
			}
		}
	}

	ready = true;
	return true;
}

double InstanceOvertime::calcEchelonDemand( int j, int t ) {
	double dk = demand[j][t];
	int k;

	for (int i = 0; i < successorCount[j]; i++) {
		k = successor[j][i];
		dk += resourceCost[j][k] * calcEchelonDemand(k, t);
	}

	return dk;
}

double InstanceOvertime::calcTotalEchelonDemand( int j, int t ) {
	double dk = 0;
	int k;

	for (int p = t; p < T; p++)
		dk += eDemand[j][p];

	for (int i = 0; i < successorCount[j]; i++) {
		k = successor[j][i];
		dk += resourceCost[j][k] * calcTotalEchelonDemand(k, t);
	}

	return dk;
}

char* InstanceOvertime::getPath( const char *name ) {
	char *path = new char[ strlen(name) + strlen(DIR) + strlen(EXT) + strlen(SUBDIR) + 5];
	char type = name[0] + ('a' - 'A');

	sprintf(path, "%s%s/%c/%s%s", DIR, SUBDIR, type, name, EXT);
	return path;
}

int InstanceOvertime::getSolSize() throw (NotReady) {
	return J*T;
}

int InstanceOvertime::getSolCols() throw (NotReady) {
	return T;
}

int InstanceOvertime::getSolRows() throw (NotReady) {
	return J;
}

void InstanceOvertime::print() throw (NotReady) {
	cout << name << endl;
	cout << "Products:\t" << J << endl <<
			"Periods:\t" << T << endl <<
			"Machines:\t" << M << endl << endl;
}